//
// This file is part of cpp-lib, a library of C++ functions and classes.
// Cpp-lib is copyright (C) 2004 and onwards, The CPL Interest Group.
// 
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or (at
// your option) any later version.
// 
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
// General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this library (see the file COPYING); if not, write to the
// Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
// 02111-1307  USA
//
// $Id$
//

#include <exception>
#include <stdexcept>
#include <string>
#include <sstream>

#include <cmath>
#include <cstring>
#include <cerrno>

#include <sys/time.h>
#include <unistd.h>

#include "cpp-lib/posix/wrappers.h"
#include "cpp-lib/realtime.h"
  
using namespace cpl::detail_ ;


namespace {

int const MY_TIMER  = ITIMER_REAL ;
int const MY_SIGNAL = SIGALRM     ;


// Wait for signal s, discarding any information.

inline void wait4( ::sigset_t const& ss ) {
	
  int dummy ;
  STRERROR_CHECK( ::sigwait( &ss , &dummy ) ) ;

}


} // anonymous namespace


cpl::util::realtime_scheduler::realtime_scheduler( double const& dt ) {

  always_assert( dt > 0 ) ;

  sigs = block_signal( MY_SIGNAL ) ;

  // Set up timer
  
  ::itimerval itv ;
  itv.it_value    = to_timeval( 1e-6 ) ; // arm: need nonzero value
  itv.it_interval = to_timeval( dt   ) ;

  STRERROR_CHECK
  ( ::setitimer( MY_TIMER , &itv , 0 ) ) ;

}


double cpl::util::realtime_scheduler::time() {

  ::timeval t ;
  ::gettimeofday( &t , 0 ) ;

  return to_double( t ) ;

}
    

double cpl::util::realtime_scheduler::wait_next() {

  wait4( sigs ) ;
  return time() ;

}


cpl::util::realtime_scheduler::~realtime_scheduler() { 
  
  // Disable our timer.
  ::itimerval itv ;
  itv.it_value    = to_timeval( 0 ) ;
  itv.it_interval = to_timeval( 0 ) ;

  STRERROR_CHECK
  ( ::setitimer( MY_TIMER , &itv , 0 ) ) ;

}
